require 'byebug'

module PackerPellat
  class Cube
    attr_accessor :x, :y, :lenght, :width, :height
    attr_reader :acreage, :volume

    def initialize(x = 0, y = 0, lenght, width, height)
      @x, @y, @lenght, @width, @height = x, y, lenght, width, height
      @acreage = @lenght * @width
      @volume = @acreage * @height
    end
  end

  class Pellat < Cube
    attr_reader :rotate     # 是否可旋转
    attr_accessor :direct   # 货物摆放方向，是否有旋转
    def initialize(x = 0, y = 0, lenght, width, height, rotate = false)
      super(x, y, lenght, width, height)
      @rotate = rotate
      @direct = false
    end

    def <=>(other)
      result = volume <=> other.volume
      result = acreage <=> other.acreage if result.zero?
      return result unless result.zero?
      rotate ? (other.rotate ? 0 : 1) : (other.rotate ? -1 : 0)
    end
  end

  class Wrapper < Cube
    attr_accessor :right, :down, :used

    def initialize(x = 0, y = 0, lenght, width, height)
      super(x, y, lenght, width, height)
      @used = nil
    end

    def split(pellat)
      (width >= pellat.width) && (height >= pellat.height)

      result = (volume >= pellat.volume) && (acreage >= pellat.acreage) && (height >= pellat.height)
      return result unless result

      result = (lenght >= pellat.lenght) && (width >= pellat.width)
      if !result && pellat.rotate
        result = (lenght >= pellat.width) && (width >= pellat.lenght)
        pellat.direct = result if result
      end

      result && fill_pellat(pellat)
    end

    # 填充货物信息
    def fill_pellat(pellat)
      @used = pellat
      @right = self.class.new(x + pellat.height, y, height - pellat.height, pellat.acreage)
      @down = self.class.new(x, y + pellat.acreage, height, acreage - pellat.acreage)
      pellat.x, pellat.y = x, y
    end

    def find(pellat)
      if used
        right.find(pellat) || down.find(pellat)
      elsif split(pellat)
        self
      else
        nil
      end
    end

    def fit(pellats)
      pellats.sort_by {|prev, current| current <=> prev}

      pellats.each {|pellat| return nil unless find(pellat)}
      pellats
    end
  end
end

def test
  wrap = PackerPellat::Wrapper.new 500, 500
  pellats = [
    PackerPellat::Pellat.new(400, 400),
    PackerPellat::Pellat.new(500, 50),
    PackerPellat::Pellat.new(50, 400)
  ]
  rtn = wrap.fit pellats
  puts "result: #{rtn}"
end

test
